﻿// ===============================================================================
// Generator.cs
// EOS Framework
// ===============================================================================
// Copyright (c) EOS Development Group. All rights reserved.
// ===============================================================================

namespace EOS.Framework.Data.Generator
{
    using System.Collections.Generic;
    using System.Data.SqlClient;
    using System.Linq;
    using EOS.Framework.Schema.Extensions;
    using EOS.Framework.Schema.MSSQL;
    using EOS.Framework.Tools;
    using EOS.Framework.Contracts.Schema;

    public class Generator
    {
        public static bool IsCrossTable(TableSchema tableToCheck)
        {
            var primaryKeys = from c in tableToCheck.Columns where c.IsPrimaryKey == true select c;

            return (tableToCheck.Columns.Count == 2 && primaryKeys.Count() == 2 && tableToCheck.InReferences.Count == 2);
        }

        public void StartGeneration(string connectionString, string targetNamespace, string targetAssembly, string directory, bool geneateDAOClasses)
        {
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();

                ISchemaProvider schemaProvider = new MSSQLSchemaProvider(connection);

                foreach (TableSchema table in schemaProvider.ExtractSchema(false).Tables)
                {
                    if (IsCrossTable(table))
                    {
                        continue;
                    }
                
                    EntityGenerator entityGenerator = new EntityGenerator(targetNamespace, StringHelper.MakeSingularPascalCase(table.Name), table.Name);
                    MappingGenerator mappingGenerator = new MappingGenerator(targetAssembly, targetNamespace, StringHelper.MakeSingularPascalCase(table.Name), table.Name);
                    DaoInterfaceGenerator daoInterfaceGenerator = new DaoInterfaceGenerator(targetNamespace, StringHelper.MakeSingularPascalCase(table.Name), table.Name);
                    DaoGenerator daoGenerator = new DaoGenerator(targetNamespace, StringHelper.MakeSingularPascalCase(table.Name), table.Name);

                    foreach (ColumnSchema column in table.Columns)
                    {
                        if (column.IsReference() == true && column.IsPrimaryKey == false)
                        {
                            continue;
                        }

                        if (!column.IsPrimaryKey)
                        {
                            mappingGenerator.AddDatabaseField(
                                column.Name.ToPascalCase(),
                                column.Name, column.DataType.ToString(), column.Length, column.AllowNull);
                        }
                        else
                        {
                            mappingGenerator.AddDatabasePrimaryKey(
                                column.Name.ToPascalCase(),
                                column.Name, column.DataType.ToString(), column.IsIdentity);
                        }

                        entityGenerator.AddDatabaseField(
                            column.Name.ToPascalCase(),
                            column.Name.ToCamelCase(), column.DataType.ToString());
                    }

                    foreach (ReferenceSchema reference in table.InReferences)
                    {
                        string propertyName = reference.ParentTable.Name;
                        if (reference.ParentTable == table)
                        {
                            propertyName = "parent";
                        }

                        entityGenerator.AddDatabaseInReference(
                            StringHelper.MakeSingularPascalCase(reference.Joins[0].ChildColumn.Name),
                            StringHelper.MakeSingularCamelCase(reference.Joins[0].ChildColumn.Name),
                            StringHelper.MakeSingularPascalCase(reference.ParentTable.Name));

                        mappingGenerator.AddDatabaseInReference(
                            StringHelper.MakeSingularPascalCase(reference.Joins[0].ChildColumn.Name),
                            reference.Joins[0].ChildColumn.Name,
                            StringHelper.MakeSingularPascalCase(reference.ParentTable.Name));
                    }

                    foreach (ReferenceSchema reference in table.OutReferences)
                    {
                        string referencedTable = StringHelper.MakeSingularPascalCase(reference.ChildTable.Name);
                        
                        string columnName = null;
                        if (IsCrossTable(reference.ChildTable))
                        {
                            if (reference == reference.ChildTable.InReferences[0])
                            {
                                columnName = reference.ChildTable.InReferences[1].Joins[0].ChildColumn.Name;
                                referencedTable = reference.ChildTable.InReferences[1].ParentTable.Name;
                            }
                            else
                            {
                                columnName = reference.ChildTable.InReferences[0].Joins[0].ChildColumn.Name;
                                referencedTable = reference.ChildTable.InReferences[0].ParentTable.Name;
                            }
                        }

                        string propertyName = referencedTable;
                        if (reference.ChildTable == table)
                        {
                            propertyName = "children";
                        }

                        entityGenerator.AddDatabaseOutReference(
                            StringHelper.MakePluralPascalCase(propertyName),
                            StringHelper.MakePluralCamelCase(propertyName),
                            StringHelper.MakeSingularPascalCase(referencedTable));

                        List<string> keys = new List<string>();
                        foreach (ReferenceJoin join in reference.Joins)
                        {
                            keys.Add(join.ChildColumn.Name);
                        }

                        mappingGenerator.AddDatabaseOutReference(
                            StringHelper.MakePluralPascalCase(propertyName), keys,
                            reference.ChildTable.Name,
                            StringHelper.MakeSingularPascalCase(referencedTable),
                            columnName, IsCrossTable(reference.ChildTable));
                    }

                    entityGenerator.SaveToFile(directory + StringHelper.MakeSingularPascalCase(table.Name) + ".cs");
                    mappingGenerator.SaveToFile(directory + StringHelper.MakeSingularPascalCase(table.Name) + ".hbm.xml");

                    if (geneateDAOClasses)
                    {
                        daoGenerator.SaveToFile(directory + StringHelper.MakeSingularPascalCase(table.Name) + "Dao.cs");
                        daoInterfaceGenerator.SaveToFile(directory + "I" + StringHelper.MakeSingularPascalCase(table.Name) + "Dao.cs");
                    }
                }
            }            
        }
    }
}
